package com.zyx.javademo.stream;

import com.google.common.collect.Lists;
import com.zyx.javademo.bean.Product;
import lombok.Data;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Yaxi.Zhang
 * @since 2022/7/8 18:30
 * desc: https://www.cnblogs.com/henuyuxiang/p/14989223.html
 * https://www.jianshu.com/p/60390d9c3301
 */
public class GroupByDemo2 {
    @Data
    public static class User {
        private Integer id;
        private Integer schoolId;
        private String userName;
        private String edu;
        private double price;
    }

    private List<Product> getProdList() {
        Product prod1 = new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");
        Product prod2 = new Product(2L, 2, new BigDecimal("20"), "饼干", "零食");
        Product prod3 = new Product(3L, 3, new BigDecimal("30"), "月饼", "零食");
        Product prod4 = new Product(4L, 3, new BigDecimal("10"), "青岛啤酒", "啤酒");
        Product prod5 = new Product(5L, 10, new BigDecimal("15"), "百威啤酒", "啤酒");
        return Lists.newArrayList(prod1, prod2, prod3, prod4, prod5);
    }

    private List<User> getUsers() {
        List<User> users = new ArrayList<>();
        User u1 = new User();
        u1.setId(1001);
        u1.setSchoolId(100);
        u1.setUserName("小1");
        u1.setEdu("001");
        u1.setPrice(0.01);

        User u2 = new User();
        u2.setId(1002);
        u2.setSchoolId(100);
        u2.setUserName("小2");
        u2.setEdu("002");
        u2.setPrice(0.20);

        User u3 = new User();
        u3.setId(2010);
        u3.setSchoolId(200);
        u3.setUserName("小3");
        u3.setEdu("001");
        u3.setPrice(3.00);

        User u4 = new User();
        u4.setId(3001);
        u4.setSchoolId(300);
        u4.setEdu("001");
        u4.setPrice(40.0);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);

        return users;
    }


    @Test
    public void test1() {
        List<Product> prodList = getProdList();
        // 按照类目分组
        Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory));
        System.out.println(prodMap);
    }


    @Test
    public void test2() {
        List<Product> prodList = getProdList();
        // 按照几个属性分组
        Map<String, List<Product>> prodMap =
                prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));
        System.out.println(prodMap);
    }

    @Test
    public void test3() {
        List<Product> prodList = getProdList();
        // 根据不同条件分组
        Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(item -> {
            if (item.getNum() < 3) {
                return "3";
            } else {
                return "other";
            }
        }));
        System.out.println(prodMap);
    }

    // ================================= 多级分组 =================================
    @Test
    public void test4() {
        List<Product> prodList = getProdList();
        // 多级分组　　
        // 要实现多级分组，我们可以使用一个由双参数版本的Collectors.groupingBy工厂方法创 建的收集器
        // 它除了普通的分类函数之外，还可以接受collector类型的第二个参数。
        // 那么要进 行二级分组的话，我们可以把一个内层groupingBy传递给外层groupingBy，并定义一个为流 中项目分类的二级标准。
        Map<String, Map<String, List<Product>>> prodMap = prodList.stream().collect(Collectors
                .groupingBy(Product::getCategory, Collectors.groupingBy(item -> {
                    if (item.getNum() < 3) {
                        return "3";
                    } else {
                        return "other";
                    }
                })));
        System.out.println(prodMap);
    }

    // ================================= 按子组收集数据 =================================
    @Test
    public void test5() {
        List<Product> prodList = getProdList();
        // 求总数
        Map<String, Long> prodMap1 = prodList.stream()
                .collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
        System.out.println(prodMap1);

        // 求和
        Map<String, Integer> prodMap2 = prodList.stream()
                .collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
        System.out.println(prodMap2);


        // 把收集器的结果转换为另一种类型
        Map<String, Product> prodMap3 = prodList.stream().collect(Collectors
                .toMap(Product::getCategory, Function.identity(), BinaryOperator.maxBy(Comparator.comparingInt(Product::getNum))));
        System.out.println(prodMap3);

        // 联合其他收集器
        Map<String, Set<String>> prodMap4 = prodList.stream()
                .collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));

        System.out.println(prodMap4);

    }

    // 需要三个参数，第三个参数添加了对结果Map的生成方式，默认是HashMap
    @Test
    public void test6() {
        List<User> users = getUsers();
        users.forEach(System.out::println);
        Map <String,Double > collect = users.stream().collect(Collectors.groupingBy(User::getEdu,
                // 决定map的生成方式，使用TreeMap
                TreeMap::new,
                // 对参数进行累计求和
                Collectors.summingDouble(User::getPrice)));
        System.out.println(collect);
    }

}
